MODULE_NAME='ProliphixV5'
(
	Dev dvStat,
	Dev vdvStat,
	Dev dvDisplayDevice[],
	Integer nPanelType[],
        Integer nDisplayDeviceStat[],
	Integer nChans[],
	Integer nAddress[],
	Integer nStartUpOffset
)
(***********************************************************)
(***********************************************************)
(*  FILE_LAST_MODIFIED_ON: 04/04/2006  AT: 11:33:16        *)
(***********************************************************)
(* System Type : NetLinx                                   *)
(***********************************************************)
(* REV HISTORY:                                            *)
(***********************************************************)
(*
    $History: $
*)    
(***********************************************************)
(*          DEVICE NUMBER DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_DEVICE

(***********************************************************)
(*               CONSTANT DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_CONSTANT
cnGet[] = 'get'
cnSet[] = 'pdp'
cnOID_ThermAverageTemp[]    = '4.1.13='
cnOID_ThermSensor1Temp[]    = '4.3.2.1='
cnOID_ThermSensor2Temp[]    = '4.3.2.2='
cnOID_ThermSensor3Temp[]    = '4.3.2.3='
cnOID_ThermHumidity[]       = '4.1.14='
cnOID_ThermHVACMode[]       = '4.1.1='
cnOID_ThermHVACState[]      = '4.1.2='
cnOID_ThermFanMode[]        = '4.1.3='
cnOID_ThermFanState[]       = '4.1.4='
cnOID_ThermSetBackHeat[]    = '4.1.5='
cnOID_ThermSetBackCool[]    = '4.1.6='
cnOID_ThermHumiditySet[]    = '4.2.22='
cnOID_ThermSetBackStatus[]  = '4.1.9='
cnOID_ThermCurrentPeriod[]  = '4.1.10='
cnOID_ThermActivePeriod[]   = '4.1.12='
cnOID_ThermCurrentClass[]   = '4.1.11='
cnOID_ThermAlarm1Status[]   = '1.13.2.1='
cnOID_ThermAlarm2Status[]   = '1.13.2.2='
cnOID_ThermAlarm3Status[]   = '1.13.2.3='
cnOID_ThermDeviceName[]     = '1.2='
cnOID_ThermSensor2Name[]    = '4.3.5.2='
cnOID_ThermSensor3Name[]    = '4.3.5.3='
cnOID_ThermSetHoldState[] = "'4.1.22='"
cnOID_ThermScheduleSpecial[]= "'4.4.4.6'"


cnBtn_HeatSetRaise     = 1
cnBtn_HeatSetLower     = 2
cnBtn_CoolSetRaise     = 3
cnBtn_CoolSetLower     = 4
cnBtn_HumiditySetRaise = 5
cnBtn_HumiditySetLower = 6
cnBtn_ModeToggle       = 7
cnBtn_ModeOff          = 8
cnBtn_ModeHeat         = 9
cnBtn_ModeCool         = 10
cnBtn_ModeAuto         = 11
cnBtn_FanToggle        = 12
cnBtn_FanAuto          = 13
cnBtn_FanOn            = 14
cnBtn_FanSchedule      = 15
cnBtn_ThermDeviceName  = 16
cnBtn_ClassOccupied    = 17
cnBtn_ClassUnoccupied  = 18
cnBtn_ClassOther       = 19
cnBtn_HoldModeToggle   = 20
cnBtn_HoldModeOn       = 21
cnBtn_HoldModeOff      = 22

cnAddr_ThermAverageTemp   = 1
cnAddr_ThermSensor1Temp   = 2
cnAddr_ThermSensor2Temp   = 3
cnAddr_ThermSensor3Temp   = 4
cnAddr_ThermHumidity      = 5
cnAddr_ThermHVACMode      = 6
cnAddr_ThermHVACState     = 7
cnAddr_ThermFanMode       = 8
cnAddr_ThermFanState      = 9
cnAddr_ThermSetBackHeat   = 10
cnAddr_ThermSetBackCool   = 11
cnAddr_ThermHumiditySet   = 12
cnAddr_ThermSetBackStatus = 13
cnAddr_ThermCurrentPeriod = 14
cnAddr_ThermActivePeriod  = 15
cnAddr_ThermCurrentClass  = 16
cnAddr_ThermAlarm1Status  = 17
cnAddr_ThermAlarm2Status  = 18
cnAddr_ThermAlarm3Status  = 19
cnAddr_ThermDeviceName    = 20
cnAddr_ThermSensor2Name   = 21
cnAddr_ThermSensor3Name   = 22

cnClassOccupied   = 1
cnClassUnoccupied = 2
cnClassOther      = 3

cnProliphix_TimelineID = 1
cnProliphix_TimelineQty = 3
Long cnProliphixTimelineTimes[] = {18000,18000,18000}

ENCR_MAX_ENC_LEN = 1000
ENCR_MAX_DEC_LEN = 750

cnPanelType_DMS    = 1
cnPanelType_MIODMS = 2
cnPanelType_TP     = 3
cnPanelType_WEB    = 4
(***********************************************************)
(*              DATA TYPE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_TYPE

(***********************************************************)
(*               VARIABLE DEFINITIONS GO BELOW             *)
(***********************************************************)
DEFINE_VARIABLE
//#DEFINE DEBUG

Volatile Char strInputBuffer[1000]
Volatile Char strOutputBuffer[500]
Volatile SInteger nResult
Volatile Char strIPAddress[25]
Volatile long nPort = 80
Volatile Integer nStatNum = 0
Volatile Integer nNumOfDisplays = 0
Volatile Char strUserName[25] = 'admin'
Volatile Char strPassword[25] = 'admin'
Volatile Dev vdvFirstWebDevice

Volatile Sinteger nAverageTemp
Volatile Sinteger nSensor1Temp
Volatile Sinteger nSensor2Temp
Volatile Sinteger nSensor3Temp
Volatile Integer nHumidity
Volatile Integer nHVACMode
Volatile Integer nHVACState
Volatile Integer nFanMode
Volatile Integer nFanState = 1
Volatile Float fHeatSet
Volatile Float fCoolSet
Volatile Float nHumiditySet
Volatile Integer nSetBackStatus
Volatile Integer nCurrentPeriod
Volatile Integer nActivePeriod
Volatile Integer nCurrentClass
Volatile Integer nAlarm[3]
Volatile Char strDeviceName[20]
Volatile Char strSensor2Name[20]
Volatile Char strSensor3Name[20]
Volatile Integer nHoldMode

Volatile Char strHVACMode[4][10]  = {'Off', 'Heat', 'Cool', 'Auto'}
Volatile Char strHVACState[8][15] = {'Initializing', 'Off', 'Heat1', 'Heat2', 'Reset', 'Cool1', 'Cool2', 'Delay'}
Volatile Char strFanMode[3][10]   = {'Auto', 'On', 'Schedule'}
Volatile Char strFanState[3][10]  = {'Init', 'Off', 'On'}
Volatile Char strSetBackStatus[3][10] = {'Normal', 'Hold', 'Override'}
Volatile Char strCurrentPeriod[4][10] = {'Morning', 'Day', 'Evening', 'Night'}
Volatile Char strActivePeriod[6][20] = {'Scheduled Morning', 'Scheduled Day', 'Scheduled Evening', 'Scheduled Night', 'Hold', 'Temp Override'}
Volatile Char strCurrentClass[3][10] = {'Occupied', 'Unoccupied', 'Custom'}
Volatile Char strAlarm[3][20]     = {'No Alarm/Reminder', 'Yellow Alarm', 'Alarm Pending'}

IP_ADDRESS_STRUCT sMyIPAddress (* .Flags *)
(* .HostName *)
(* .IPAddress *)
(* .SubnetMask *)
(* .Gateway *)

(* Base 64 *)
VOLATILE CHAR cEncrBase64[] = {
                               'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',   //  0 to  7
                               'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',   //  8 to 15
                               'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',   // 16 to 23
                               'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f',   // 24 to 31
                               'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n',   // 32 to 39
                               'o', 'p', 'q', 'r', 's', 't', 'u', 'v',   // 40 to 47
                               'w', 'x', 'y', 'z', '0', '1', '2', '3',   // 48 to 55
                               '4', '5', '6', '7', '8', '9', '+', '/'    // 56 to 63
                              }

(***********************************************************)
(*        SUBROUTINE/FUNCTION DEFINITIONS GO BELOW         *)
(***********************************************************)
(* EXAMPLE: DEFINE_FUNCTION <RETURN_TYPE> <NAME> (<PARAMETERS>) *)
(* EXAMPLE: DEFINE_CALL '<NAME>' (<PARAMETERS>) *)
Define_Call 'OUTPUT_CMD' (Char strCmd[], Char strOID[])
{
	strOutputBuffer = "'GET /',strCmd,'?',strOID,' HTTP/1.1',13,10
	            ,'Content-Type: application/x-www-form-urlencoded',13,10
							,'User-Agent: Jakarta Commons-HttpClient/2.0.2',13,10
	            ,'Host: ',sMyIPAddress.IPAddress,13,10
							,'Connection: Keep-Alive',13,10
							,'Authorization: Basic ',EncrBase64Encode("strUserName,':',strPassword"),13,10,13,10"

    If (nResult != 0)  // Not connected
    {
        nResult=IP_Client_Open(dvStat.Port,strIPAddress,nPort,1)  // IP Client Connect TCP
    }
    Else
    {
        Call 'DEBUG_OUTPUT' (__LINE__,"' strOutputBuffer:',strOutputBuffer")
        Send_String dvStat,"strOutputBuffer"
    }
}

Define_Call 'DEBUG_OUTPUT' (Integer nLine, Char strData[])
{
	#IF_DEFINED DEBUG
	Send_String 0,"__FILE__,' Line:',Itoa(nLine),' ',strData"
	#END_IF
}

Define_Call 'UPDATE_UI_SINGLE' (dev dvUIDevice, Integer nDevIndex )
{
    If (nAddress[cnAddr_ThermDeviceName])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermDeviceName], strDeviceName)
    }
    If (nAddress[cnAddr_ThermAverageTemp])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermAverageTemp], itoa(nAverageTemp))
    }
    If (nAddress[cnAddr_ThermSensor1Temp])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSensor1Temp], itoa(nSensor1Temp))
    }
    If (nAddress[cnAddr_ThermSensor2Temp])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSensor2Temp], itoa(nSensor2Temp))
    }
    If (nAddress[cnAddr_ThermSensor3Temp])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSensor3Temp], itoa(nSensor3Temp))
    }
    If (nAddress[cnAddr_ThermHumidity])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermHumidity], Itoa(nHumidity))
    }
    If (nAddress[cnAddr_ThermHVACMode])
    {
        Call 'UPDATE_MODE_FEEDBACK_SINGLE' (dvUIDevice)
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermHVACMode], Itoa(strHVACMode[nHVACState]))
    }
    If (nAddress[cnAddr_ThermHVACState])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermHVACState], Itoa(strHVACState[nHVACState]))
    }
    If (nAddress[cnAddr_ThermFanMode])
    {
        Call 'UPDATE_FAN_FEEDBACK_SINGLE' (dvUIDevice)
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermFanMode], Itoa(strFanMode[nFanState]))
    }
    If (nAddress[cnAddr_ThermFanState])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermFanState], Itoa(strFanState[nFanState]))
    }
    If (nAddress[cnAddr_ThermSetBackHeat])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSetBackHeat], Ftoa(fHeatSet))
    }
    If (nAddress[cnAddr_ThermSetBackCool])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSetBackCool], Ftoa(fCoolSet))
    }
    If (nAddress[cnAddr_ThermHumiditySet])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermHumiditySet], Itoa(nHumiditySet))
    }
    If (nAddress[cnAddr_ThermSetBackStatus])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermSetBackStatus], strSetBackStatus[nSetBackStatus])
    }
    If (nAddress[cnAddr_ThermCurrentPeriod])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermCurrentPeriod], strCurrentPeriod[nCurrentPeriod])
    }
    If (nAddress[cnAddr_ThermActivePeriod])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermActivePeriod], strActivePeriod[nActivePeriod])
    }
    If (nAddress[cnAddr_ThermCurrentClass])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermCurrentClass], strCurrentClass[nCurrentClass])
    }
    If (nAddress[cnAddr_ThermAlarm1Status])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermAlarm1Status], strAlarm[nAlarm[1]])
    }
    If (nAddress[cnAddr_ThermAlarm2Status])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermAlarm2Status], strAlarm[nAlarm[2]])
    }
    If (nAddress[cnAddr_ThermAlarm3Status])
    {
        Call 'V_TEXT_SINGLE' (dvUIDevice, nPanelType[nDevIndex], nAddress[cnAddr_ThermAlarm3Status], strAlarm[nAlarm[3]])
    }
}

Define_Call 'V_TEXT_SINGLE' (dev dvDevice, Integer nPanelType, Integer nAddr, Char strVarText[])
{
	Switch (nPanelType)
	{
		Case cnPanelType_DMS:
		Case cnPanelType_MIODMS:
		Case cnPanelType_TP:
		{
			Send_Command dvDevice,"'TEXT',Itoa(nAddr),'-',strVarText"
			Call 'DEBUG_OUTPUT' (__LINE__,"'TEXT',Itoa(nAddr),'-',strVarText")
		}
		Case cnPanelType_WEB:
		{
			Send_Command vdvFirstWebDevice,"'TEXT',Itoa(dvDevice.Number),',',Itoa(nAddr),'-',strVarText"
			Call 'DEBUG_OUTPUT' (__LINE__,"'TEXT',Itoa(dvDevice.Number),',',Itoa(nAddr),'-',strVarText")
		}
		Default:
		{
			Call 'DEBUG_OUTPUT' (__LINE__,"'V_TEXT_SINGLE Did not recognize panel type',Itoa(nPanelType)")
		}
	}
}

Define_Call 'V_TEXT_MULTI' (dev dvDevice[], Integer nPanelType[], Integer nAddr, Char strVarText[])
{
    Stack_Var Integer nI
    Call 'DEBUG_OUTPUT' (__LINE__,"'TEXT',Itoa(nAddr),'-',strVarText")
    For (nI = 1; nI <= Length_Array(nPanelType); nI++)
    {
        //send_string 0,"'nPanelType[',itoa(nI),'] = ',itoa(nPanelType[nI])"

	// SMM Only send when the current UI is displaying this stat
        if( nDisplayDeviceStat[nI] == nStatNum )
        {
            Switch (nPanelType[nI])
            {
                Case cnPanelType_DMS:
                Case cnPanelType_MIODMS:
                Case cnPanelType_TP:
                {
                    Send_Command dvDevice[nI],"'TEXT',Itoa(nAddr),'-',strVarText"
                    //Call 'DEBUG_OUTPUT' (__LINE__,"'TEXT',Itoa(nAddr),'-',strVarText")
                }
                Case cnPanelType_WEB:
		{
                    Send_Command vdvFirstWebDevice,"'TEXT',Itoa(dvDevice[nI].Number),',',Itoa(nAddr),'-',strVarText"
                    //Call 'DEBUG_OUTPUT' (__LINE__,"'TEXT',Itoa(dvDevice[nI].Number),',',Itoa(nAddr),'-',strVarText")
                }
                Default:
		{
		    Call 'DEBUG_OUTPUT' (__LINE__,"'V_TEXT_MULTI Did not recognize panel type',Itoa(nPanelType[nI])")
                }
            }
        }
    }
}

Define_Call 'UPDATE_MODE_FEEDBACK_SINGLE' (Dev dvDevice)
{
	Stack_Var Integer nI
	For (nI = 1; nI <= 4; nI++)
	{
		If (nHVACMode == nI)
		{
			On[dvDevice,nChans[cnBtn_ModeOff+nI-1]]
		}
		Else
		{
			Off[dvDevice,nChans[cnBtn_ModeOff+nI-1]]
		}
	}
}

Define_Call 'UPDATE_MODE_FEEDBACK_MULTI' (Dev dvDevice[])
{
	Stack_Var Integer nI
	For (nI = 1; nI <= 4; nI++)
	{
		If (nHVACMode == nI)
		{
			On[dvDevice,nChans[cnBtn_ModeOff+nI-1]]
		}
		Else
		{
			Off[dvDevice,nChans[cnBtn_ModeOff+nI-1]]
		}
	}
}

Define_Call 'UPDATE_FAN_FEEDBACK_SINGLE' (Dev dvDevice)
{
	Stack_Var Integer nI
	For (nI = 1; nI <= 3; nI++)
	{
		If (nFanMode == nI)
		{
			On[dvDevice,nChans[cnBtn_FanAuto+nI-1]]
		}
		Else
		{
			Off[dvDevice,nChans[cnBtn_FanAuto+nI-1]]
		}
	}
}

Define_Call 'UPDATE_FAN_FEEDBACK_MULTI' (Dev dvDevice[])
{
    Stack_Var Integer nI
    For (nI = 1; nI <= 3; nI++)
    {
        If (nFanMode == nI)
        {
            On[dvDevice,nChans[cnBtn_FanAuto+nI-1]]
        }
        Else
        {
            Off[dvDevice,nChans[cnBtn_FanAuto+nI-1]]
        }
    }
}

Define_call 'UPDATE_CURRENT_CLASS_FEEDBACK_MULTI' (Dev dvDevice[])
{
    switch( nCurrentClass )
    {
	case cnClassOccupied:
        {
            off[dvDevice,nChans[cnBtn_ClassUnoccupied]]
            off[dvDevice,nChans[cnBtn_ClassOther]]
            on[dvDevice,nChans[cnBtn_ClassOccupied]]
        }
	case cnClassUnoccupied:
        {
            off[dvDevice,nChans[cnBtn_ClassOccupied]]
            off[dvDevice,nChans[cnBtn_ClassOther]]
            on[dvDevice,nChans[cnBtn_ClassUnoccupied]]
        }
	case cnClassOther:
        {
            off[dvDevice,nChans[cnBtn_ClassOccupied]]
            off[dvDevice,nChans[cnBtn_ClassUnoccupied]]
            on[dvDevice,nChans[cnBtn_ClassOther]]
        }
    }
}

Define_Call 'UPDATE_HOLD_FEEDBACK_MULTI' (Dev dvDevice[])
{
    If (nHoldMode == 2)
    {
        On[dvDevice,nChans[cnBtn_HoldModeToggle]]
    }
    Else
    {
        Off[dvDevice,nChans[cnBtn_HoldModeToggle]]
    }
}

DEFINE_FUNCTION CHAR[100] STRING_REPLACE(CHAR strSTR[],CHAR strSEARCH[],CHAR strREPLACE[])
STACK_VAR
INTEGER nPOS
CHAR strTRASH[100]
CHAR strTEMP_STR[100]
{
  (* QUICK OUT *)
  nPOS = FIND_STRING(strSTR, "strSEARCH", 1)
  IF (!nPOS)
    RETURN strSTR;

  (* LOOP AND REPLACE *)
  WHILE (nPOS)
  {
    (* REBUILD STRING AND REPLACE NEW CHARACTER *)
    strTEMP_STR = ""
    IF (nPOS > 1)
      strTEMP_STR = LEFT_STRING(strSTR, nPOS - 1)
    strTRASH = REMOVE_STRING(strSTR, "strTEMP_STR,strSEARCH", 1)
    strSTR = "strTEMP_STR,strREPLACE,strSTR"
    (* MAKE WE START AT nPOS + LEN OF REPLACE OR WE LOOP FOREVER *)
    (* AND BLOW THE STACK IF WE REPLACE WITH PORTION OF SEARCH! *)
    nPOS = FIND_STRING(strSTR, "strSEARCH", nPOS + LENGTH_STRING(strREPLACE))
  }
  RETURN strSTR;
}

(****************************)
(* NAME: EncrBase64Encode   *)
(***********************************************************)
(***********************************************************)
DEFINE_FUNCTION CHAR[ENCR_MAX_ENC_LEN] EncrBase64Encode(CHAR cDecStr[])
{
STACK_VAR CHAR    cDecodeStr[ENCR_MAX_DEC_LEN], cEncodeStr[ENCR_MAX_ENC_LEN]
STACK_VAR INTEGER nLoop, nTemp
STACK_VAR CHAR    cIdx1, cIdx2, cIdx3, cIdx4, cByte1, cByte2, cByte3, cChar1, cChar2, cChar3, cChar4
  // Copy string and pad
  cDecodeStr = cDecStr
  nTemp = LENGTH_STRING(cDecodeStr) % 3
  IF (nTemp <> 0)
  {
    cDecodeStr[LENGTH_STRING(cDecodeStr)+1] = 0
    cDecodeStr[LENGTH_STRING(cDecodeStr)+2] = 0
    cDecodeStr[LENGTH_STRING(cDecodeStr)+3] = 0
  }

  // Encode
  FOR (cEncodeStr = "", nLoop = 1; nLoop <= LENGTH_STRING(cDecodeStr); nLoop = nLoop + 3)
  {
    // Get bytes
    cByte1 = cDecodeStr[nLoop]
    cByte2 = cDecodeStr[nLoop+1]
    cByte3 = cDecodeStr[nLoop+2]

    // Get index
    cIdx1  =  TYPE_CAST(((cByte1 & $FC) >> 2) + 1)
    cIdx2  =  TYPE_CAST((((cByte2 & $F0) >> 4) | ((cByte1 & $03) << 4)) + 1)
    cIdx3  =  TYPE_CAST((((cByte3 & $C0) >> 6) | ((cByte2 & $0F) << 2)) + 1)
    cIdx4  =  TYPE_CAST(  (cByte3 & $3F) + 1)

    // Get chars
    cChar1 = cEncrBase64[cIdx1]
    cChar2 = cEncrBase64[cIdx2]
    cChar3 = cEncrBase64[cIdx3]
    cChar4 = cEncrBase64[cIdx4]

    // Pad?
    IF (LENGTH_STRING(cDecodeStr) < (nLoop+1))
      cChar3 = $3D // '='
    IF (LENGTH_STRING(cDecodeStr) < (nLoop+2))
      cChar4 = $3D // '='

    // Build string
    cEncodeStr = "cEncodeStr, cChar1, cChar2, cChar3, cChar4"
  }
  RETURN cEncodeStr;
}

DEFINE_FUNCTION CHAR[100] GET_IP_ERROR (LONG lERR)
{
  SELECT
  {
    ACTIVE (lERR = 0):
      RETURN "";
    ACTIVE (lERR = 2):
      RETURN "'IP ERROR (',ITOA(lERR),'): General Failure (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
    ACTIVE (lERR = 4):
      RETURN "'IP ERROR (',ITOA(lERR),'): unknown host (IP_CLIENT_OPEN)'";
    ACTIVE (lERR = 6):
      RETURN "'IP ERROR (',ITOA(lERR),'): connection refused (IP_CLIENT_OPEN)'";
    ACTIVE (lERR = 7):
      RETURN "'IP ERROR (',ITOA(lERR),'): connection timed out (IP_CLIENT_OPEN)'";
    ACTIVE (lERR = 8):
      RETURN "'IP ERROR (',ITOA(lERR),'): unknown connection error (IP_CLIENT_OPEN)'";
    ACTIVE (lERR = 14):
      RETURN "'IP ERROR (',ITOA(lERR),'): local port already used (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
    ACTIVE (lERR = 16):
      RETURN "'IP ERROR (',ITOA(lERR),'): too many open sockets (IP_CLIENT_OPEN/IP_SERVER_OPEN)'";
    ACTIVE (lERR = 10):
      RETURN "'IP ERROR (',ITOA(lERR),'): Binding error (IP_SERVER_OPEN)'";
    ACTIVE (lERR = 11):
      RETURN "'IP ERROR (',ITOA(lERR),'): Listening error (IP_SERVER_OPEN)'";
    ACTIVE (lERR = 15):
      RETURN "'IP ERROR (',ITOA(lERR),'): UDP socket already listening (IP_SERVER_OPEN)'";
    ACTIVE (lERR = 9):
      RETURN "'IP ERROR (',ITOA(lERR),'): Already closed (IP_CLIENT_CLOSE/IP_SERVER_CLOSE)'";
    ACTIVE (1):
      RETURN "'IP ERROR (',ITOA(lERR),'): Unknown'";
  }
}

(***********************************************************)
(*                STARTUP CODE GOES BELOW                  *)
(***********************************************************)
DEFINE_START
//Create_Buffer dvStat, strInputBuffer

Wait (600+nStartUpOffset) // let the system fire up first
{
	Get_Ip_Address(0:0:0,sMyIPAddress) // Populates the struct sMyIPAddress
	nResult = 1 // This needs to be a non zero number since 0 is a good conenction
	Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermDeviceName,'&OID',cnOID_ThermSensor2Name,'&OID',cnOID_ThermSensor3Name")

	// Get tne number of display devices for this device
        nNumOfDisplays = length_array(dvDisplayDevice)

	Timeline_Create(cnProliphix_TimelineID, cnProliphixTimelineTimes, cnProliphix_TimelineQty, Timeline_Relative, Timeline_Repeat)
}

(***********************************************************)
(*                THE EVENTS GO BELOW                      *)
(***********************************************************)
DEFINE_EVENT

Timeline_Event[cnProliphix_TimelineID]
{
	Switch(Timeline.Sequence)
	{
		Case 1:
		{
			Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermAverageTemp,
			                           '&OID',cnOID_ThermSensor1Temp,
						   '&OID',cnOID_ThermSensor2Temp,
						   '&OID',cnOID_ThermSensor3Temp,
						   '&OID',cnOID_ThermHumidity")
		}
		Case 2:
		{
			Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermHVACMode,
						  '&OID',cnOID_ThermHVACState,
						  '&OID',cnOID_ThermFanMode,
						  '&OID',cnOID_ThermFanState,
						  '&OID',cnOID_ThermSetBackHeat,
						  '&OID',cnOID_ThermSetBackCool,
						  '&OID',cnOID_ThermHumiditySet")
		}
		Case 3:
		{
			Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermSetBackStatus,
						  '&OID',cnOID_ThermCurrentPeriod,
						  '&OID',cnOID_ThermActivePeriod,
						  '&OID',cnOID_ThermCurrentClass,
						  '&OID',cnOID_ThermAlarm1Status,
						  '&OID',cnOID_ThermAlarm2Status,
						  '&OID',cnOID_ThermAlarm3Status")
		}
	}
}

Data_Event[vdvStat]
{
  Online:
  {
  }
  Offline:
  {
  }
	Command:
	{
		Stack_Var Char strCmd[25]
		Stack_Var Integer nDeviceIndex

		Call 'DEBUG_OUTPUT' (__LINE__,"'Data.Text:',Data.Text")
		If (Find_String(Data.Text,'=',1))
		{
			strCmd = Upper_String(Remove_String(Data.Text,'=',1))
			Switch (strCmd)
			{
				Case 'IPADDRESS=':
				{
					strIPAddress = Data.Text
				}
			        Case 'PORT=':
                                {
                                        nPort = type_cast( atoi( Data.Text ) )
				}
				Case 'USERNAME=':
				{
					strUserName = Data.Text
				}
                                case 'STATNUMBER=':
                                {
                                        nStatNum = type_cast( atoi( Data.Text ) )
                                }
                                case 'UPDATE=':
                                {
                                    nDeviceIndex = type_cast( atoi( Data.Text ) )
                                    nDisplayDeviceStat[nDeviceIndex] = nStatNum

                                    call 'UPDATE_UI_SINGLE' ( Data.Device, nDeviceIndex )

                                }
                                case 'CLEARUPDATE=':
				{
                                    nDeviceIndex = type_cast( atoi( Data.Text ) )
                                    nDisplayDeviceStat[nDeviceIndex] = 0
                                }                              
//				Case 'FIRSTWEBDEVICE=':
//				{
//					vdvFirstWebDevice.Number = Atoi(Data.Text)
//					Remove_String (Data.Text,':',1)
//					vdvFirstWebDevice.Port = Atoi(Data.Text)
//					Remove_String (Data.Text,':',1)
//					vdvFirstWebDevice.System = Atoi(Data.Text)
//					Call 'DEBUG_OUTPUT' (__LINE__,"'vdvFirstWebDevice:',Itoa(vdvFirstWebDevice.Number),':',Itoa(vdvFirstWebDevice.Port),':',Itoa(vdvFirstWebDevice.System)")
//				}
				Case 'PASSWORD=':
				{
					strPassword = Data.Text
				}
				Case 'HEAT=':
				{
					fHeatSet = Atof(Data.Text)
					If (fHeatSet >= 45 && fHeatSet <= 95)
					{
						Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackHeat,Ftoa(fHeatSet*10)")
					}
				}
				Case 'COOL=':
				{
					fCoolSet = Atof(Data.Text)
					If (fCoolSet >= 45 && fCoolSet <= 95)
					{
						Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackCool,Ftoa(fCoolSet*10)")
					}
				}
				Case 'HUMIDITY=':
				{
					nHumiditySet = Atoi(Data.Text)
					If (nHumiditySet >= 45 && nHumiditySet <= 95)
					{
						Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHumiditySet,Ftoa(nHumiditySet*10)")
					}
				}
			}
		}
	}
}

Data_Event[dvStat]
{
  OnError:
  {
    strInputBuffer=""
    If (Data.Number != 14)
      nResult = 1  // set connection state to a invalid number (0 = connected)
    Call 'DEBUG_OUTPUT' (__LINE__,"GET_IP_ERROR (Data.Number)")
  } 
  Online:
  {
		If (Length_String(strOutputBuffer))
		{
			Call 'DEBUG_OUTPUT' (__LINE__,"' strOutputBuffer:',strOutputBuffer")
			Send_String dvStat,"strOutputBuffer"
		}
  }
  Offline:
  {
    nResult = 1  // set connection state to a invalid number (0 = connected)
  }
	String:
	{
		Stack_Var Char strCmd[10], strValue[50], strDebugBuffer[500]
		
		strInputBuffer = Data.Text
		strDebugBuffer = strInputBuffer
		While (Find_String (strDebugBuffer,"13,10",1))
		{
			Call 'DEBUG_OUTPUT' (__LINE__,"'strDebugBuffer:',Remove_String(strDebugBuffer,"13,10",1)")
		}
		Call 'DEBUG_OUTPUT' (__LINE__,"'strDebugBuffer:',strDebugBuffer")
		
		While (Find_String(strInputBuffer, 'OID', 1))
		{
			Remove_String(strInputBuffer, 'OID', 1)
			If (Find_String(strInputBuffer, '=', 1))
			{
				strCmd = Remove_String(strInputBuffer, '=', 1)
				Call 'DEBUG_OUTPUT' (__LINE__,"'strCmd:',strCmd")
				If (Find_String(strInputBuffer, '&', 1))
				{
					strValue = Remove_String(strInputBuffer, '&', 1)
					strValue = Left_String(strValue, Length_String(strValue)-1)
				}
				Else
				{	
					strValue = strInputBuffer
				}
				Call 'DEBUG_OUTPUT' (__LINE__,"'strValue:',strValue")
				Switch (strCmd)
				{
					Case cnOID_ThermAverageTemp:
					{
						nAverageTemp = Atoi(strValue)/10
						If (nAddress[cnAddr_ThermAverageTemp])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermAverageTemp], itoa(nAverageTemp))
						}
					}
					Case cnOID_ThermSensor1Temp:
					{
						nSensor1Temp = Atoi(strValue)/10
						If (nAddress[cnAddr_ThermSensor1Temp])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSensor1Temp], itoa(nSensor1Temp))
						}
					}
					Case cnOID_ThermSensor2Temp:
					{
						nSensor2Temp = Atoi(strValue)/10
						If (nAddress[cnAddr_ThermSensor2Temp])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSensor2Temp], Ftoa(nSensor2Temp))
						}
					}
					Case cnOID_ThermSensor3Temp:
					{
						nSensor3Temp = Atoi(strValue)/10
						If (nAddress[cnAddr_ThermSensor3Temp])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSensor3Temp], itoa(nSensor3Temp))
						}
					}
					Case cnOID_ThermHumidity:
					{
						nHumidity = Atoi(strValue)
						If (nAddress[cnAddr_ThermHumidity])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermHumidity], Itoa(nHumidity))
						}
					}
					Case cnOID_ThermHVACMode:
					{
						nHVACMode = Atoi(strValue)
						Call 'UPDATE_MODE_FEEDBACK_MULTI' (dvDisplayDevice)
						If (nAddress[cnAddr_ThermHVACMode])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermHVACMode], strHVACMode[nHVACMode])
						}
					}
					Case cnOID_ThermHVACState:
					{
						nHVACState = Atoi(strValue)
						If (nAddress[cnAddr_ThermHVACState])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermHVACState], strHVACState[nHVACState])
						}
					}
					Case cnOID_ThermFanMode:
					{
						nFanMode = Atoi(strValue)
						Call 'UPDATE_FAN_FEEDBACK_MULTI' (dvDisplayDevice)
						If (nAddress[cnAddr_ThermFanMode])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermFanMode], strFanMode[nFanMode])
						}
					}
					Case cnOID_ThermFanState:
					{
						nFanState = Atoi(strValue) + 1 // compensate for the zero index
						If (nAddress[cnAddr_ThermFanState])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermFanState], strFanState[nFanState])
						}
					}
					Case cnOID_ThermSetBackHeat:
					{
						fHeatSet = Atof(strValue)/10
						If (nAddress[cnAddr_ThermSetBackHeat])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSetBackHeat], Ftoa(fHeatSet))
						}
					}
					Case cnOID_ThermSetBackCool:
					{
						fCoolSet = Atof(strValue)/10
						If (nAddress[cnAddr_ThermSetBackCool])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSetBackCool], Ftoa(fCoolSet))
						}
					}
					Case cnOID_ThermHumiditySet:
					{
						nHumiditySet = Atoi(strValue)
						If (nAddress[cnAddr_ThermHumiditySet])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermHumiditySet], Itoa(nHumiditySet))
						}
					}
					Case cnOID_ThermSetBackStatus:
					{
						nSetBackStatus = Atoi(strValue)
                                                call 'UPDATE_HOLD_FEEDBACK_MULTI' (dvDisplayDevice)
						If (nAddress[cnAddr_ThermSetBackStatus])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSetBackStatus], strSetBackStatus[nSetBackStatus])                                                        
						}
					}
					Case cnOID_ThermCurrentPeriod:
					{
						nCurrentPeriod = Atoi(strValue)
						If (nAddress[cnAddr_ThermCurrentPeriod])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermCurrentPeriod], strCurrentPeriod[nCurrentPeriod])
						}
					}
					Case cnOID_ThermActivePeriod:
					{
						nActivePeriod = Atoi(strValue)
						If (nAddress[cnAddr_ThermActivePeriod])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermActivePeriod], strActivePeriod[nActivePeriod])
						}
					}
					Case cnOID_ThermCurrentClass:
					{
						nCurrentClass = Atoi(strValue)
						If (nAddress[cnAddr_ThermCurrentClass])
						{
                                                        call 'UPDATE_CURRENT_CLASS_FEEDBACK_MULTI' (dvDisplayDevice)
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermCurrentClass], strCurrentClass[nCurrentClass])
						}
					}
					Case cnOID_ThermAlarm1Status:
					{
						nAlarm[1] = Atoi(strValue)
						If (nAddress[cnAddr_ThermAlarm1Status])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermAlarm1Status], strAlarm[nAlarm[1]])
						}
					}
					Case cnOID_ThermAlarm2Status:
					{
						nAlarm[2] = Atoi(strValue)
						If (nAddress[cnAddr_ThermAlarm2Status])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermAlarm2Status], strAlarm[nAlarm[2]])
						}
					}
					Case cnOID_ThermAlarm3Status:
					{
						nAlarm[3] = Atoi(strValue)
						If (nAddress[cnAddr_ThermAlarm3Status])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermAlarm3Status], strAlarm[nAlarm[3]])
						}
					}
					Case cnOID_ThermDeviceName:
					{
						strDeviceName = strValue
						If (nAddress[cnAddr_ThermDeviceName])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermDeviceName], strDeviceName)
						}
					}
					Case cnOID_ThermSensor2Name:
					{
						strSensor2Name = strValue
						If (nAddress[cnAddr_ThermSensor2Name])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSensor2Name], strSensor2Name)
						}
					}
					Case cnOID_ThermSensor3Name:
					{
						strSensor3Name = strValue
						If (nAddress[cnAddr_ThermSensor3Name])
						{
							Call 'V_TEXT_MULTI' (dvDisplayDevice, nPanelType, nAddress[cnAddr_ThermSensor3Name], strSensor3Name)
						}
					}
				}
			}
			Else
			{
				Call 'DEBUG_OUTPUT' (__LINE__,"'Could not find a = after OID.  strInputBuffer:',strInputBuffer")
			}
		}
	}
}

Data_Event[dvDisplayDevice]
{
    Online:
    {
        Stack_Var Integer nDeviceIndex
        nDeviceIndex = get_last(dvDisplayDevice)

        // Only update the UI that came online if it currently has a stat selected.
        if( nDisplayDeviceStat[nDeviceIndex] == nStatNum )
        {
            call 'UPDATE_UI_SINGLE' ( Data.Device, nDeviceIndex )
        }
    }
}

Button_Event[dvDisplayDevice,nChans]
{
    Push:
    {
        Stack_Var Integer nBtnIndex
        nBtnIndex = Get_Last(nChans)
        Switch (nBtnIndex)
        {
            Case cnBtn_HeatSetRaise:
            {
                If (fHeatSet < 45)
                {
		    fHeatSet = 45
                }
                Else If (fHeatSet < 95)
                {
	            fHeatSet++
                }
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackHeat,Ftoa(fHeatSet*10)")
            }
            Case cnBtn_HeatSetLower:
            {
                If (fHeatSet <= 45)
                {
                    fHeatSet = 45
		}
                Else
                {
                    fHeatSet--
                }
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackHeat,Ftoa(fHeatSet*10)")
            }
            Case cnBtn_CoolSetRaise:
            {
		If (fCoolSet < 45)
		{
			fCoolSet = 45
		}
		Else If (fCoolSet < 95)
		{
			fCoolSet++
		}
		Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackCool,Ftoa(fCoolSet*10)")
            }
            Case cnBtn_CoolSetLower:
            {
                If (fCoolSet <= 45)
                {
		    fCoolSet = 45
                }
                Else
                {
                    fCoolSet--
                }
                
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackCool,Ftoa(fCoolSet*10)")
            }
            Case cnBtn_HumiditySetRaise:
            {
                If (nHumiditySet <= 5)
		{
                    nHumiditySet = 5
                }
                Else If (nHumiditySet < 95)
                {
                    nHumiditySet++
                }
    
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHumiditySet,Ftoa(nHumiditySet)")
            }
            Case cnBtn_HumiditySetLower:
            {
		If (nHumiditySet <= 5)
                {
		    nHumiditySet = 5
                }
                Else
                {
		    nHumiditySet--
                }
                
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHumiditySet,Ftoa(nHumiditySet)")
            }
            Case cnBtn_ModeToggle:
            {
                If (nHVACMode >= 4)
		    nHVACMode = 1
		Else
                   nHVACMode++
		
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHVACMode,Itoa(nHVACMode)")
	    }
            Case cnBtn_ModeOff:
            {
		Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHVACMode,'1'")
            }
            Case cnBtn_ModeHeat:
            {
		Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHVACMode,'2'")
            }
            Case cnBtn_ModeCool:
            {
		Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHVACMode,'3'")
            }
            Case cnBtn_ModeAuto:
            {
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermHVACMode,'4'")
            }
            Case cnBtn_FanToggle:
	    {
                If (nFanMode >= 3)
                     nFanMode = 1
		Else
                    nFanMode++
                
		Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermFanMode,Itoa(nFanMode)")
            }
            Case cnBtn_FanAuto:
            {
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermFanMode,'1'")
	    }
            Case cnBtn_FanOn:
            {
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermFanMode,'2'")
            }
            Case cnBtn_FanSchedule:
            {
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermFanMode,'3'")
	    }
            case cnBtn_ThermDeviceName:
            {
                Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermDeviceName,'&OID',cnOID_ThermSensor2Name,'&OID',cnOID_ThermSensor3Name")
            }
            case cnBtn_ClassOccupied:
            {
                send_string 0,"'INSIDE OCCUPIED'"
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermScheduleSpecial,'.',itoa(cnClassOccupied)")
		wait 10
                    Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermCurrentClass")
            }
            case cnBtn_ClassUnoccupied:
            {
                send_string 0,"'INSIDE UNOCCUPIED'"
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermScheduleSpecial,'.',itoa(cnClassUnoccupied)")
		wait 10
                    Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermCurrentClass")
            }
            case cnBtn_ClassOther:
            {
                send_string 0,"'INSIDE OTHER'"
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermScheduleSpecial,'.',itoa(cnClassOther)")
                wait 10
                    Call 'OUTPUT_CMD' (cnGet, "'OID',cnOID_ThermCurrentClass")
            }
            case cnBtn_HoldModeToggle:
            {
                If (nHoldMode >= 2)
                    nHoldMode = 1
		Else
                    nHoldMode++

                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackStatus,Itoa(nHoldMode)")
            }
            case cnBtn_HoldModeOn:
            {
                nHoldMode = 2
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackStatus,Itoa(nHoldMode)")
            }
            case cnBtn_HoldModeOff:
            {
                nHoldMode = 1
                Call 'OUTPUT_CMD' (cnSet, "'OID',cnOID_ThermSetBackStatus,Itoa(nHoldMode)")
            }
        }
    }
}

(***********************************************************)
(*            THE ACTUAL PROGRAM GOES BELOW                *)
(***********************************************************)
DEFINE_PROGRAM

(***********************************************************)
(*                     END OF PROGRAM                      *)
(*        DO NOT PUT ANY CODE BELOW THIS COMMENT           *)
(***********************************************************)
